package org.ly817.my.spring.core.ioc.factory;

import org.ly817.my.spring.core.ioc.bean.BeanDefinition;
import org.ly817.my.spring.core.ioc.bean.PropertyValue;
import org.ly817.my.spring.core.ioc.bean.enhance.BeanFactoryAware;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 自动装配实现
 * <p>
 *
 * @author LuoYu
 * @date 2018/4/7
 */
public class AutowireCapableBeanFactory extends AbstractBeanFactory {


    /**
     * 将bean定义信息（BeanDefinition）中的非bean引用属性注入到bean实体中
     * 调用链：getBean-doCreateBean-applyPropertyValues
     * 调用情况:
     * 1.初始化后，未调用preInstantiateSingletons批量实例化
     * 2.使用preInstantiateSingletons批量实例化过程中
     * <p>
     * 循环依赖
     *
     * @param bean
     * @param beanInfo
     */
    @Override
    protected void applyPropertyValues(Object bean, BeanDefinition beanInfo) throws Exception {
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        for (PropertyValue propertyValue : beanInfo.getBeanPropertyValues().getPropertyValues()) {
            Object value = propertyValue.getValue();
            if (propertyValue.getBeanReference() != null) {
//            if (value instanceof PropertyValue.BeanReference) {
//                PropertyValue.BeanReference beanReference = (PropertyValue.BeanReference) value;
                // TODO 如何解决循环依赖
                value = getBean(propertyValue.getBeanReference().getRefBeanName());
            }

            try {
                String methodName = "set" + propertyValue.getName().substring(0, 1).toUpperCase()
                        + propertyValue.getName().substring(1);
                // 拼接set方法，注入属性到bean中
                Method declaredMethod = bean.getClass().getDeclaredMethod(
                        methodName, value.getClass());
                declaredMethod.setAccessible(true);

                declaredMethod.invoke(bean, value);
            } catch (NoSuchMethodException e) {
                Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
                declaredField.setAccessible(true);
                declaredField.set(bean, value);
            }
        }
    }
}
